import tkinter
from tkinter import messagebox
import json, time
import threading
import select
from socket import *
from common import client_draw
from common import rsa_operation


class ChatRoom(object):
    def connect(self):  # 配置连接
        self.s = socket(AF_INET, SOCK_STREAM)
        remote_host = gethostname()  # 获取计算机名称
        port = 1300  # 设置端口号
        self.s.connect((remote_host, port))  # 发起连接
        print("from %s connected to %s" % (self.s.getsockname(), self.s.getpeername()))
        return self.s

    def recive(self, s):  # 监听消息
        self.my = [s]
        while True:
            rs, ws, es = select.select(self.my, [], [])
            if s in rs:
                try:
                    data = s.recv(1024).decode('utf-8')
                    data_dict = json.loads(data)
                    type = data_dict["type"]  # 根据服务端返回的type值，进入不同逻辑
                    if type == "login":  # 登录逻辑
                        if "000000" == data_dict["code"]:  # code返回000000，跳转聊天页面
                            # 保存服务器返回的公匙
                            rsa_operation.save_pub_key(data_dict["pubkey"])

                            nickname = data_dict["nickname"]
                            self.chat_interface(nickname)
                        else:
                            tkinter.messagebox.showinfo(title='login info', message=data_dict["msg"])
                    elif type == "register":  # 注册逻辑
                        if "000000" == data_dict["code"]:  # code返回000000，跳转聊天页面
                            nickname = data_dict["nickname"]
                            tkinter.messagebox.showinfo(title='enter chatroom', message=data_dict["msg"])
                            self.chat_interface(nickname)
                        else:
                            tkinter.messagebox.showinfo(title='register info', message=data_dict["msg"])
                    elif type == "chat":  # 聊天逻辑
                        # 解密服务器发来的信息
                        message = rsa_operation.client_decrypt(data_dict["message"])
                        # 验证信息有没有被篡改
                        if not rsa_operation.client_verify(message, data_dict["sign"]):
                            print('服务器信息被篡改....,已拦截')
                            continue

                        nickname = data_dict["nickname"]
                        isMy = data_dict["isMy"]
                        times = " " + nickname + "\t" + time.strftime("%H:%M:%S", time.localtime()) + '\n'
                        self.txtMsgList.insert(tkinter.END, times, "DimGray")  # 聊天页面，发送人以及发送时间展示
                        if "yes" == isMy:  # 如果是自己发的消息，字体使用'DarkTurquoise'，如果是别人发的消息，字体使用'Black'
                            self.txtMsgList.insert(tkinter.END, " " + message + "\n\n", 'DarkTurquoise')
                        else:
                            self.txtMsgList.insert(tkinter.END, " " + message + "\n\n", 'Black')
                        self.txtMsgList.see(tkinter.END)  # 插入消息时，自动滚动到底部

                except Exception as e:
                    print(e)
                    exit()

    def register_interface(self):  # 进入注册界面
        client_draw.draw_register(self)

    def chat_interface(self, nickname):  # 进入聊天页面
        client_draw.draw_chat(self, nickname)

    def return_login_interface(self):  # 返回登录页面
        self.label_nickname.destroy()  # 将不需要的label_nickname控件先销毁
        self.input_nickname.destroy()  # 将不需要的input_nickname控件先销毁
        self.label_password.destroy()  # 将不需要的label_password控件先销毁
        self.input_password.destroy()  # 将不需要的input_password控件先销毁
        client_draw.draw_login(self)

    def verify_register(self):  # 获取输入框内容，进行注册验证
        account = self.input_account.get()
        password = self.input_password.get()
        nickname = self.input_nickname.get()
        try:
            register_data = {}
            register_data["type"] = "register"
            register_data["account"] = account
            register_data["password"] = password
            register_data["nickname"] = nickname
            data = json.dumps(register_data)  # 将register_data由dict格式转为json字符串，便于网络传输
            self.s.send(data.encode('utf-8'))
        except Exception as e:
            print(e)

    def verify_login(self):  # 获取输入框内容，进行登录信息验证
        account = self.input_account.get()
        password = self.input_password.get()
        try:
            login_data = {}
            login_data["type"] = "login"
            login_data["account"] = account
            login_data["password"] = password
            # 将自己的公钥发送给服务器
            login_data["pubkey"] = rsa_operation.get_client_public_key()

            data = json.dumps(login_data)  # 将login_data由dict格式转为json字符串，便于网络传输
            self.s.send(data.encode('utf-8'))
        except Exception as e:
            print(e)

    def sendMsg(self):  # 获取输入框内容，发送消息
        message = self.txtMsg.get('0.0', tkinter.END).strip()
        if not message:
            tkinter.messagebox.showinfo(title='发送提示', message="can not send empty message!")
            return
        self.txtMsg.delete('0.0', tkinter.END)
        try:
            chat_data = {}
            chat_data["type"] = "chat"
            # 用服务器公匙加密信息
            chat_data["message"] = rsa_operation.client_encrypt(message)
            # 对消息进行签名
            chat_data["sign"] = rsa_operation.client_sign(message)

            data = json.dumps(chat_data)  # 将chat_data由dict格式转为json字符串，便于网络传输
            self.s.send(data.encode('utf-8'))
        except Exception as e:
            print(e)

    def sendMsgEvent(self, event):  # 发送消息事件
        if event.keysym == 'Return':  # 如果捕捉到键盘的回车按键，触发消息发送
            self.sendMsg()

    def on_closing(self):  # 聊天页面，点击右上角退出时执行
        if messagebox.askokcancel("info", "are you sure exit？"):
            self.root.destroy()


def main():
    rsa_operation.generate_client_key()

    chatRoom = ChatRoom()
    client = chatRoom.connect()
    t = threading.Thread(target=chatRoom.recive, args=(client,))  # 创建一个线程，监听消息
    t.start()
    chatRoom.root = tkinter.Tk()  # 创建主窗口,用于容纳其它组件
    client_draw.draw_login(chatRoom)  # 登录界面控件创建、布局
    tkinter.mainloop()  # 进入事件（消息）循环


if __name__ == '__main__':
    main()
